import redisManager from './redisManager.js'
import { md5 } from './helper.js'
import { REDIS_PREFIXES, WS_COMMANDS, CLOSE_CODES, HEARTBEAT_CONFIG } from '../constants/index.js'

/**
 * WebSocket 连接管理器
 */
class ConnectionManager {
  constructor() {
    this.connMap = new Map()
    this.heartbeatTimer = null
  }

  /**
   * 安全发送消息
   */
  safeSend(conn, payload) {
    try {
      conn.send(JSON.stringify(payload))
    } catch (error) {
      console.log('发送消息失败:', error.message)
    }
  }

  /**
   * 添加连接
   */
  addConnection(connId, conn) {
    this.connMap.set(connId, conn)
  }

  /**
   * 移除连接
   */
  removeConnection(connId) {
    this.connMap.delete(connId)
  }

  /**
   * 获取连接
   */
  getConnection(connId) {
    return this.connMap.get(connId)
  }

  /**
   * 处理新连接
   */
  async handleNewConnection(conn, req) {
    const { roomId, uid } = this.parseConnectionParams(req)
    
    if (!this.validateConnectionParams(roomId, uid)) {
      conn.close(CLOSE_CODES.INVALID_URL, 'invalid url')
      return null
    }

    // 设置连接属性
    this.setupConnection(conn)
    
    // 生成连接ID
    const connId = this.generateConnectionId(roomId, uid)
    conn.connId = connId

    // 处理重复连接
    if (!(await this.handleDuplicateConnection(connId, conn))) {
      return null
    }

    // 添加连接映射
    this.addConnection(connId, conn)

    return { connId, roomId, uid }
  }

  /**
   * 处理连接关闭
   */
  async handleConnectionClose(connId) {
    try {
      const conn = this.getConnection(connId)
      if (!conn || conn.cleanedUp) {
        this.removeConnection(connId)
        return
      }

      // 清理连接映射
      this.removeConnection(connId)
      
      // 获取连接信息
      const connInfo = await redisManager.getConnectionInfo(connId)
      if (!connInfo) {
        console.log('客户端已关闭（未入房）')
        return
      }
      
      const { roomId, uid } = connInfo
      await this.cleanupUserFromRoom(roomId, uid, connId)
      
      console.log(`客户端已关闭，移除 ${uid} 来自房间 ${roomId}`)
    } catch (error) {
      console.error('连接关闭处理失败:', error.message)
    }
  }

  /**
   * 启动心跳检测
   */
  startHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer)
    }

    this.heartbeatTimer = setInterval(() => {
      this.performHeartbeat()
    }, HEARTBEAT_CONFIG.INTERVAL_MS)
  }

  /**
   * 停止心跳检测
   */
  stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer)
      this.heartbeatTimer = null
    }
  }

  /**
   * 执行心跳检测
   */
  async performHeartbeat() {
    try {
      // 如果没有连接，清理 Redis 中的过期数据
      if (this.connMap.size === 0) {
        await redisManager.cleanupStaleConnections()
        return
      }

      // 检查每个连接
      for (const [connId, client] of this.connMap.entries()) {
        if (client.isAlive === false) {
          try {
            client.terminate()
          } catch (error) {
            // 忽略终止错误
          }
          continue
        }

        client.isAlive = false
        try {
          client.ping()
        } catch (error) {
          // 忽略 ping 错误
        }

        // 检查连接是否超时
        await this.checkConnectionTimeout(client)
      }
    } catch (error) {
      console.error('心跳检测失败:', error.message)
    }
  }

  /**
   * 检查连接超时
   */
  async checkConnectionTimeout(client) {
    const lastActive = client.lastActiveAt || 0
    if (Date.now() - lastActive > HEARTBEAT_CONFIG.TIMEOUT_MS) {
      if (!client.cleanedUp) {
        client.cleanedUp = true
        await this.cleanupStaleConnection(client)
      }
    }
  }

  /**
   * 清理过期连接
   */
  async cleanupStaleConnection(client) {
    try {
      const staleConnId = client.connId
      if (!staleConnId) return

      // 从映射中删除
      this.removeConnection(staleConnId)
      
      // 获取连接信息
      const connInfo = await redisManager.getConnectionInfo(staleConnId)
      if (connInfo) {
        const { roomId, uid } = connInfo
        await this.cleanupUserFromRoom(roomId, uid, staleConnId)
      }
    } catch (error) {
      console.error('清理过期连接失败:', error.message)
    } finally {
      try {
        client.terminate()
      } catch (error) {
        // 忽略终止错误
      }
    }
  }

  /**
   * 解析连接参数
   */
  parseConnectionParams(req) {
    const url = new URL(req.url, 'http://dummyhost')
    const queryParams = url.searchParams
    return {
      roomId: queryParams.get('roomId'),
      uid: queryParams.get('uid')
    }
  }

  /**
   * 验证连接参数
   */
  validateConnectionParams(roomId, uid) {
    return roomId && roomId !== '' && uid && uid !== ''
  }

  /**
   * 设置连接属性
   */
  setupConnection(conn) {
    conn.isAlive = true
    conn.lastActiveAt = Date.now()
    conn.cleanedUp = false
    
    conn.on('pong', () => {
      conn.isAlive = true
      conn.lastActiveAt = Date.now()
    })
  }

  /**
   * 生成连接ID
   */
  generateConnectionId(roomId, uid) {
    return roomId + '_' + md5(uid)
  }

  /**
   * 处理重复连接
   */
  async handleDuplicateConnection(connId, conn) {
    const prevConn = this.getConnection(connId)
    if (prevConn) {
      const isPrevOpen = prevConn.readyState === 1 && !prevConn.cleanedUp
      if (isPrevOpen) {
        try {
          conn.close(CLOSE_CODES.DUPLICATE_CONNECTION, 'duplicate connection')
        } catch (error) {
          // 忽略关闭错误
        }
        return false
      } else {
        // 旧连接不健康，清理后继续接入新连接
        try {
          prevConn.cleanedUp = true
          prevConn.terminate()
        } catch (error) {
          // 忽略终止错误
        }
        this.removeConnection(connId)
      }
    }
    return true
  }

  /**
   * 清理用户从房间
   */
  async cleanupUserFromRoom(roomId, uid, connId) {
    const roomKey = REDIS_PREFIXES.ROOM + roomId
    const callKey = REDIS_PREFIXES.CALL + roomId

    // 从房间中移除用户
    await redisManager.removeUserFromRoom(roomId, uid)
    
    // 删除连接信息
    await redisManager.deleteConnectionInfo(connId)
    
    // 处理通话状态
    const callConn = await redisManager.getCommandClient().hget(callKey, uid)
    if (callConn) {
      await this.broadcastToRoomExcept(callKey, uid, {
        cmd: WS_COMMANDS.HANG_UP,
        uid,
        roomId
      })
      await redisManager.deleteCall(roomId)
    }

    // 广播离开消息
    await this.broadcastToRoomExcept(roomKey, uid, {
      cmd: WS_COMMANDS.PEER_LEAVE,
      msg: { peerId: uid }
    })

    // 检查房间是否为空
    const isEmpty = await redisManager.isRoomEmpty(roomId)
    if (isEmpty) {
      await redisManager.deleteRoom(roomId)
    }
  }

  /**
   * 广播消息到房间内除指定用户外的所有用户
   */
  async broadcastToRoomExcept(key, exceptUid, payload) {
    try {
      const roomMembers = await redisManager.getCommandClient().hgetall(key)
      
      for (const [uid, connId] of Object.entries(roomMembers)) {
        if (uid === exceptUid) continue
        
        const connInfo = await redisManager.getConnectionInfo(connId)
        if (connInfo) {
          await redisManager.publishBroadcast(connId, payload)
        }
      }
    } catch (error) {
      console.error('房间广播失败:', error.message)
    }
  }

  /**
   * 获取连接统计信息
   */
  getStats() {
    return {
      totalConnections: this.connMap.size,
      connections: Array.from(this.connMap.keys())
    }
  }
}

export default ConnectionManager
